package cn.openea.perpartner.wxpay.service.impl;

import cn.openea.perpartner.wxpay.entity.PayOrder;
import cn.openea.perpartner.wxpay.service.AuthService;
import cn.openea.perpartner.wxpay.service.ReflectObject;
import cn.openea.perpartner.wxpay.utils.Tools;
import cn.openea.perpartner.wxpay.vo.PayVo;
import cn.openea.perpartner.wxpay.vo.R;
import com.baomidou.mybatisplus.annotation.TableField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Name: 微信支付接口鉴权
 * @author: Mr.Yun
 * @Created: 2022/10/15 21:21
 */
@Service
public class AuthServiceImpl implements AuthService {

    @Autowired
    RedisTemplate<String, Object> redisTemplate;

    @Autowired
    ReflectObject reflectObject;

    @Value("${wxpay.authsalt}")
    private String authsalt;

    @Value("${wxpay.tokenttl}")
    private long tokenttl;

    @Override
    public R getToken(PayVo payVo) throws IllegalAccessException {
        Map<String, Object> parms = reflectObject.payVoToMap(payVo);
        // 鉴权参数不参与计算
        parms.remove("token");
        parms.remove("authstr");
        // 随机8位盐
        String randomSalt = Tools.getRandom(8);
        // 将参数与随机8位盐混合再计算md5值
        String parmsbody = randomSalt + parms.toString();
        String token = Tools.md5(parmsbody);
        // 以 token 为 key 随机8位盐为 value，将内容存到redis内有效时间为5秒
        redisTemplate.opsForValue().set(token, randomSalt, tokenttl, TimeUnit.SECONDS);
        return R.ok().put("token", token).put("ttl", tokenttl + "秒");
    }

    @Override
    public boolean auth(PayVo payVo) throws IllegalAccessException {
        Map<String, Object> parms = reflectObject.payVoToMap(payVo);
        // 从参数中获取鉴权参数 token 和 authstr
        String token = (String) parms.get("token");
        String authstr = (String) parms.remove("authstr");
        if(Tools.isBlank(token) || Tools.isBlank(authstr)){
            return false;
        }
        // 从Redis先获取对应的随机值
        String randomSalt = (String) redisTemplate.opsForValue().get(token);
        if(Tools.isBlank(randomSalt)){
            return false;
        }
        // 先移除鉴权参数
        parms.remove("token");
        parms.remove("authstr");

        // 验证 token 参数是否正确，来确定 parms 数据是否被修改过
        String parmsbody = randomSalt + parms.toString();
        if(!token.equals(Tools.md5(parmsbody))){
            // 表示参数被修改过了，不能进行处理
            return false;
        }
        // 再次通过 authstr 参数验证 数据是否是从 指定 authsalt 授权盐密码 的地方请求过来的 （WxPerPartner 12 的 长度）
        String tmpstr = authsalt.charAt(2) + authsalt.charAt(8) + token + authsalt.charAt(10) + authsalt.charAt(5);
        if(!authstr.equals(Tools.md5(tmpstr))){
            // 表示请求来源不明
            return false;
        }
        // 鉴权只可以使用1次，使用后将被移除
        redisTemplate.delete(token);
        return true;
    }

    @Override
    public R validata(Object object) throws IllegalAccessException {
        // 获取 Object 的所有属性字段
        List<Field> fieldList = reflectObject.getFieldList(object);
        for (Field field : fieldList) {
            if(!field.isAnnotationPresent(TableField.class)){
                continue;
            }
            TableField annotation = field.getAnnotation(TableField.class);
            if(!"NOT_EMPTY".equals(annotation.insertStrategy().name())){
                continue;
            }
            field.setAccessible(true);
            Object value = field.get(object);
            if(value == null) {
                return R.error().put("err_code_des", "缺少必要参数："+field.getName()+"，请仔细核查.");
            }
        }
        return R.ok();
    }
}
